/*-
 * Copyright (c) 2006, 2007 FTS Team
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 4. Neither the name of the FTS Team nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      MemoryInvIndexStorage.cpp
 *
 * $FTS$
 */
#include <MemoryInvIndexStorage.hpp>

// C Includes
#include <string.h>
#include <stdlib.h>

#ifdef _DEBUG
  #include <stdio.h>
#endif

namespace FTSS // FullTextSearchSystem
{

//
// Constructor
//
MemoryInvIndexStorage::MemoryInvIndexStorage(Size_t iDictSize): oStorageHash(iDictSize), aZones(NULL), iZonesCount(0) { ;; }

//
// Store document entry in specified data storage
//
void MemoryInvIndexStorage::StoreDocumentEntry(WordId_t iWordId, DocumentEntry * pEntry, const Size_t & iDocEntrySize)
{
	// Get Word hash
	StorageHash::iterator ithData = oStorageHash.find(iWordId, iWordId);

	Offset_t iOffset = 0;
	Size_t iDF = 0;
	// Number of document entires
	DocumentEntry * pTMPEntry = pEntry;
	while (iOffset < iDocEntrySize)

	{
		iOffset += pTMPEntry -> size;
		pTMPEntry = (DocumentEntry * )((CHAR_8 *)pTMPEntry + pTMPEntry -> size);
		// Document frequency
		++iDF;
	}

	// Nothing found
	if (ithData == oStorageHash.end())
	{
		StorageValue oStorageValue;
		// DocumentFrequency + array of document entries
		oStorageValue.data_length = iDocEntrySize + sizeof(Size_t);
		oStorageValue.data        = ::operator new(sizeof(UCHAR_8) * iDocEntrySize + sizeof(Size_t));

		*((Size_t *)oStorageValue.data) = iDF;

		// Copy new data
		memcpy((UCHAR_8 *)oStorageValue.data + sizeof(Size_t), pEntry, iDocEntrySize);

		// Store data
		oStorageHash.insert(StoragePair(iWordId, oStorageValue), iWordId);
	}
	else
	{
		StorageValue oStorageValue;

		// Current data size + new data size
		oStorageValue.data_length = ithData -> second.data_length + iDocEntrySize;
		oStorageValue.data        = ::operator new(sizeof(UCHAR_8) * oStorageValue.data_length);

		// Copy old data
		memcpy(oStorageValue.data, ithData -> second.data, ithData -> second.data_length);

		// Document frequency
		iDF += *((Size_t *)(ithData -> second.data));
		*((Size_t *)oStorageValue.data) = iDF;

		::operator delete(ithData -> second.data);

		// Append new data
		memcpy((UCHAR_8 *)oStorageValue.data + ithData -> second.data_length, pEntry, iDocEntrySize);

		// Store data
		ithData -> second = oStorageValue;
	}
}

//
// Store document entry in specified data storage
//
void MemoryInvIndexStorage::StoreDocNum(const Size_t & iIDocNum) { iDocNum = iIDocNum; }

//
// Get document entry from specified data storage
//
Pair<DocumentEntryIterator, DocumentEntryIterator> MemoryInvIndexStorage::GetDocumentEntry(const WordId_t & iWordId)
{
	// Get Word hash
	StorageHash::iterator ithData = oStorageHash.find(iWordId, iWordId);
	// Return pointer to Document Entry
	if (ithData != oStorageHash.end())
	{
		Size_t iDocEntrySize = ithData -> second.data_length - sizeof(Size_t);
		return Pair<DocumentEntryIterator,
		            DocumentEntryIterator>(DocumentEntryIterator((DocumentEntry *)((UCHAR_8 *)(ithData -> second.data) + sizeof(Size_t)), 0),
		                                   DocumentEntryIterator((DocumentEntry *)((UCHAR_8 *)(ithData -> second.data) + sizeof(Size_t)), iDocEntrySize));
	}

// Nothing found, return empty pair
return Pair<DocumentEntryIterator, DocumentEntryIterator>(DocumentEntryIterator(NULL, 0), DocumentEntryIterator(NULL, 0));
}

//
// Get number of documents in collection
//
Size_t MemoryInvIndexStorage::GetDocNum() { return iDocNum; }

//
// Get document frequency for specified token
//
Size_t MemoryInvIndexStorage::GetDF(const WordId_t & iWordId)
{
	// Get Word hash
	StorageHash::iterator ithData = oStorageHash.find(iWordId, iWordId);
	// Return pointer to Document Entry
	if (ithData != oStorageHash.end())
	{
		return *((Size_t *)(ithData -> second.data));
	}
// Nothing found, return 0
return 0;
}

//
// Dump index to the disc
//
INT_32 MemoryInvIndexStorage::Dump(DataDumper * pObject)
{
	if (pObject -> StartDump(C_INV_IDX_TYPE, (CHAR_P)C_INV_IDX_READABLE) != 0) { return -1; }

	if (pObject -> Dump(&(iZonesCount), sizeof(Size_t)) != 0) { return -1; }

	if (aZones != NULL)
	{
		for (Size_t iI = 0; iI < iZonesCount; ++iI)
		{
			if (pObject -> Dump(&aZones[iI], sizeof(Zone)) != 0) { return -1; }
		}
	}

	if (pObject -> Dump(&(iDocNum), sizeof(Size_t)) != 0)              { return -1; }

	// Dump data
	StorageHash::iterator ithData = oStorageHash.begin();
	while (ithData != oStorageHash.end())
	{
		// Hash Key
		if (pObject -> Dump(&(ithData -> first), sizeof(TokenHash_t)) != 0)              { return -1; }
		// Size of DocumentEntry array
		if (pObject -> Dump(&(ithData -> second.data_length), sizeof(Size_t)) != 0)      { return -1; }
		// DocumentEntry array
		if (pObject -> Dump(ithData -> second.data, ithData -> second.data_length) != 0) { return -1; }

		ithData++;
	}
	TokenHash_t iDummy = (TokenHash_t)-1;
	if (pObject -> Dump(&iDummy, sizeof(TokenHash_t)) != 0)   { return -1; }

	if (pObject -> EndDump() != 0) { return -1; }

#ifdef _DEBUG
fprintf(stderr, "Dumped %d documents, %d zones\n", iDocNum, iZonesCount);
#endif

return 0;
}

//
// Restore index from disc
//
INT_32 MemoryInvIndexStorage::Restore(DataDumper * pObject)
{
	if (pObject -> StartRestore(C_INV_IDX_TYPE, (CHAR_P)C_INV_IDX_READABLE) != 0) { return -1; }

	if (pObject -> Restore(&(iZonesCount), sizeof(Size_t)) != 0) { return -1; }

	if (aZones != NULL) { delete [] aZones; }

	aZones = new Zone[iZonesCount];
	for (Size_t iI = 0; iI < iZonesCount; ++iI)
	{
		if (pObject -> Restore(&aZones[iI], sizeof(Zone)) != 0) { return -1; }
	}

	// Get number of documents
	if (pObject -> Restore(&iDocNum, sizeof(Size_t)) != 0) { return -1; }

	oStorageHash.clear();

	TokenHash_t iWordId = 0;
	do
	{
		if (pObject -> Restore(&iWordId, sizeof(TokenHash_t)) != 0) { return -1; }

		// End-of-index
		if (iWordId == (TokenHash_t)(-1)) { break; }

		// Size of DocumentEntry array
		Size_t iDocEntrySize = 0;
		if (pObject -> Restore(&iDocEntrySize, sizeof(Size_t)) != 0) { return -1; }

		StorageValue oStorageValue;
		oStorageValue.data_length = iDocEntrySize;
		oStorageValue.data        = ::operator new(sizeof(UCHAR_8) * iDocEntrySize);
		// Read data
		if (pObject -> Restore(oStorageValue.data , iDocEntrySize) != 0) { return -1; }

		oStorageHash.insert(StoragePair(iWordId, oStorageValue), iWordId);

	} while(iWordId != (TokenHash_t)-1);

	if (pObject -> EndRestore() != 0) { return -1; }

#ifdef _DEBUG
fprintf(stderr, "Restored %d documents, %d zones\n", iDocNum, iZonesCount);
#endif

return 0;
}

//
// Check deletion flag of document
//
INT_32 MemoryInvIndexStorage::IsDeleted(const Size_t & iIDocNum) { return 0; }

//
// Get list of all available zones in this document collection
//
Zone * MemoryInvIndexStorage::GetZones(INT_32 & iIZonesCount)
{
	iIZonesCount = iZonesCount;
return aZones;
}

//
// Get list of all available zones in this document collection
//
void MemoryInvIndexStorage::StoreZones(const Zone * aIZones, const INT_32 & iIZonesCount)
{
	if (aZones != NULL) { delete [] aZones; }
	iZonesCount = iIZonesCount;
	aZones = new Zone[iZonesCount];

	for (Size_t iI = 0; iI < iZonesCount; ++iI) { aZones[iI] = aIZones[iI]; }
}

//
// A destructor
//
MemoryInvIndexStorage::~MemoryInvIndexStorage() throw()
{
	StorageHash::iterator ithData = oStorageHash.begin();
	while (ithData != oStorageHash.end())
	{
		::operator delete(ithData -> second.data);
//fprintf(stderr, "ithData -> second.data = %p\n", ithData -> second.data);
		++ithData;
	}

	if (aZones != NULL) { delete [] aZones; }
}

} // namespace FTSS
// End.
